#include "exception.hpp"
#include "spriteanimate.hpp"
#include <errormanager.hpp>

using namespace Shiny;
using namespace sf;

/*****************
 *               *
 * SpriteAnimate *
 *               *
 *****************/
SpriteAnimate::SpriteAnimate()
{
  _currentAnimation = 0;
  _flippedX         = false;
  _flippedY         = false;
}

SpriteAnimate::SpriteAnimate(const AnimationArray& animations)
{
  _animations = animations;
  SetAnimation(0);
}

void SpriteAnimate::FlipX(bool is)
{
  if (is != _flippedX)
  {
    sf::Vector2f scale(GetScale());

    SetScale(-scale.x, scale.y);
    _flippedX = is;
  }
}

void SpriteAnimate::FlipY(bool is)
{
  if (is != _flippedY)
  {
    sf::Vector2f scale(GetScale());

    SetScale(scale.x, -scale.y);
    _flippedY = is;
  }
}

void SpriteAnimate::SetAnimations(const AnimationArray& animations)
{
  _animations = animations;
  SetAnimation(0);
}

void SpriteAnimate::SetAnimation(unsigned int id)
{
  if (id >= _animations.size())
  {
    ErrorManager::Get()->AddError("SpriteAnimate: animation id out of range", ErrorManager::Warning);
    return ;
  }
  SpriteAnimate::Animation& anim = _animations[id];

  _currentAnimation = id;
  anim.Reset();
  SetTexture(*(anim.Texture()));
  SetTextureRect(anim.Rect());
}

void SpriteAnimate::Update()
{
  if (_animations.size() > 0)
    _animations[_currentAnimation](this);
}

void SpriteAnimate::SetTextureRect(const IntRect& toSet)
{
  Sprite::SetTextureRect(toSet);
}

sf::Vector2f SpriteAnimate::GetSize(void) const
{
  sf::IntRect rect = GetTextureRect();
  
  return (sf::Vector2f(rect.Width, rect.Height));
}

/*****************
 *               *
 *   Animation   *
 *               *
 *****************/
SpriteAnimate::Animation::Animation()
{
  _clock.Restart();
  _refreshRate  = 0.04;
  _inLoop       = true;
  _currentFrame = 0;
  _image        = 0;
}

SpriteAnimate::Animation::Animation(const sf::Texture* texture)
{
  _clock.Restart();
  _image        = texture;
  _refreshRate  = 0.f;
  _inLoop       = false;
  _currentFrame = 0;
  _nFrames      = 0;
  _rect.Top     = 0;
  _rect.Left    = 0;
  _rect.Height  = texture->GetHeight();
  _rect.Width   = texture->GetWidth();
}

SpriteAnimate::Animation::Animation(const SpriteAnimate::Animation& copy)
{
  _clock.Restart();
  _image        = copy._image;
  _refreshRate  = copy._refreshRate;
  _inLoop       = copy._inLoop;
  _currentFrame = 0;
  _nFrames      = copy._nFrames;
  _rect         = copy._rect;
}

SpriteAnimate::Animation::Animation(const AnimationList::Animation& descriptor)
{
  _clock.Restart();
  _image        = descriptor._tex;
  _refreshRate  = descriptor._refreshRate;
  _inLoop       = descriptor._inLoop;
  _currentFrame = 0;
  _nFrames      = descriptor._nFrames;
  _rect         = descriptor._rect;
}

SpriteAnimate::Animation::Animation(const sf::Texture* tex, IntRect rect, unsigned short nFrames, float refresh, bool inLoop)
{
  _clock.Restart();
  _image        = tex;
  _refreshRate  = refresh;
  _inLoop       = inLoop;
  _currentFrame = 0;
  _nFrames      = nFrames;
  _rect         = rect;
}

void    SpriteAnimate::Animation::operator()(SpriteAnimate* sprite)
{
  if (_clock.GetElapsedTime().AsMilliseconds() > _refreshRate)
  {
    if (_currentFrame == _nFrames)
    {
      sprite->AnimationFinished.Emit(sprite->_currentAnimation);
      if (!(_inLoop))
	return ;
      _currentFrame = 0;
    }

    sf::IntRect rect = _rect;

    rect.Left  += _currentFrame++ * _rect.Width;
    sprite->SetTextureRect(rect);
    _clock.Restart();
  }
}

void    SpriteAnimate::Animation::SetNextFrame(SpriteAnimate* sprite)
{
  (*this)(sprite);
}

const Texture*  SpriteAnimate::Animation::Texture() const
{
  return (_image);
}

void    SpriteAnimate::Animation::SetRefreshRate(float toSet)
{
  _refreshRate = toSet;
}

void    SpriteAnimate::Animation::SetInLoop(bool toSet)
{
  _inLoop = toSet;
}

void    SpriteAnimate::Animation::Reset(void)
{
  _currentFrame = 0;
}

Vector2f SpriteAnimate::Animation::Size(void) const
{
  Vector2f size;

  size.y = _rect.Height;
  size.x = _rect.Width;
  return (size);
}

/************************
 *                      *
 *   Animation Loading  *
 *                      *
 ************************/
SpriteAnimate::AnimationArray SpriteAnimate::LoadAnimations(Data data, TextureList& texs, AnimationList& anims)
{
  Data::iterator current = data.begin();
  Data::iterator end     = data.end();
  AnimationArray array;
  
  while (current != end)
  {
    Data leave = (*current);
    if (leave == "Texture")
      LoadTexture(array, leave.Value(), texs);
    else if (leave == "Animation")
      LoadAnimation(array, leave.Value(), anims);
    ++current;
  }
  return (array);
}

void SpriteAnimate::LoadAnimation(AnimationArray& array, const std::string& name, AnimationList& anims)
{
  const AnimationList::Animation* animation = anims[name];
  
  if (animation == 0)
    ErrorManager::Get()->AddError("SpriteAnimate: unable to find animation " + name, ErrorManager::StopGame);
  else
    array.push_back(*animation);
}

void SpriteAnimate::LoadTexture(AnimationArray& array, const std::string& name, TextureList& texs)
{
  const sf::Texture* texture = texs[name];

  if (texture == 0)
    ErrorManager::Get()->AddError("SpriteAnimate: unable to find texture " + name, ErrorManager::StopGame);
  else
    array.push_back(texture);
}